/*
 * algorithms.cpp
 *
 *  Created on: 2011-09-10
 *      Author: pawel
 */
#include"algorithms.h"
#include"enumTypes.h"
#include"backtrack/backTrack.h"
#include"optAlgs/optAlgV.h"
#include"seqFeederV.h"
#include <pthread.h>
#include<limits.h>
#include<stdint.h>
#include<sys/param.h>
#include<string.h>
#include<list>

#include<memory>
#include"smart2DArray.h"
#include<Bpp/Seq/Sequence.h>

#include<stdio.h> //TODO comment this when not debugg
class align;
void* optAlgThread(void *arg){
	optAlgThreadParam *paramPtr=(optAlgThreadParam*)(arg);
	directionMatrix dirMatrix;
	optAlgParam algParam;
	algParam.gopen = paramPtr->gopen;
	algParam.gext = paramPtr->gext;
	algParam.querySeqPtr = paramPtr->querySeqPtr;
	algParam.scoreMatrixPtr = paramPtr->scoreMatrixPtr;
	algParam.bestAligns = paramPtr->bestAligns;
//	algParam.matrixBounds = paramPtr->matrixBounds;

	while(true){
		RESTART:
		pthread_mutex_lock( paramPtr->seqFeederPtr->getMutex());
			if(paramPtr->seqFeederPtr->isClosed()){
				pthread_mutex_unlock( paramPtr->seqFeederPtr->getMutex());
				break;
			}
		while(paramPtr->seqFeederPtr->isEmpty()){
			if(paramPtr->seqFeederPtr->isProductionEnded()){
				paramPtr->seqFeederPtr->setClose();
				pthread_mutex_unlock( paramPtr->seqFeederPtr->getMutex() );
				goto RESTART;
			}
			pthread_cond_wait(paramPtr->seqFeederPtr->getNotEmpty(), paramPtr->seqFeederPtr->getMutex());

		}

		try{
			algParam.dbSeqPtr = paramPtr->seqFeederPtr->getSeq();
			std::auto_ptr<seqAlignParams> smartSeqAlignParams(paramPtr->seqFeederPtr->getAddParam(algParam.dbSeqPtr->getName()));
			seqAlignParams *seqAlParamsPtr;
			seqAlParamsPtr = smartSeqAlignParams.get();
			if(seqAlParamsPtr != NULL){
				algParam.matrixBounds = seqAlParamsPtr->getBoundsRef();
					//printf("%s %i %i \n",algParam.dbSeqPtr->getName().c_str(),algParam.matrixBounds.minDiag,algParam.matrixBounds.maxDiag);
			}
			else{

				algParam.matrixBounds.maxDiag= INT_MAX;
				algParam.matrixBounds.minDiag = INT_MIN;
			}
			//algParam.matrixBounds.maxDiag= INT_MAX;
			//algParam.matrixBounds.minDiag = INT_MIN;
			//assert(algParam.matrixBounds.maxDiag > algParam.matrixBounds.minDiag);
		}catch(std::exception& e){
			pthread_mutex_unlock( paramPtr->seqFeederPtr->getMutex() );
			pthread_cond_signal (paramPtr->seqFeederPtr->getNotFull());
			continue;
		}
		pthread_mutex_unlock( paramPtr->seqFeederPtr->getMutex() );
		pthread_cond_signal (paramPtr->seqFeederPtr->getNotFull());

		dirMatrix.rows = paramPtr->querySeqPtr->size() + 1;
		dirMatrix.cols = algParam.dbSeqPtr->size() +1;

		try{
			smart2DArray<char> smartDirMatrix(dirMatrix.rows,dirMatrix.cols);
			dirMatrix.matrix = smartDirMatrix.getPtr();
			/*
			 * Direction matrix initialization
			 */
			dirMatrix.matrix[0][0] = DR_STOP;
			for(size_t i=1;i<dirMatrix.rows;i++){
				memset(dirMatrix.matrix[i],0,dirMatrix.cols*sizeof(char));
				dirMatrix.matrix[i][0] = DR_STOP;
			}
			for(size_t i =1;i<dirMatrix.cols;i++){
				dirMatrix.matrix[0][i] = DR_STOP;
			}

			algParam.dirMatrix = dirMatrix;
			std::list<cell> bestAlList;
			bestAlList = paramPtr->optAlgClassPtr->run(algParam);
			for(std::list<cell>::iterator it= bestAlList.begin();it != bestAlList.end();++it){
				try{
					btrack::backTrack(algParam,*it,*paramPtr);
				}catch(std::exception& e){
					throw;
				};

			}
		}catch(std::exception& e){
			if(algParam.dbSeqPtr != NULL)delete algParam.dbSeqPtr;
					break; //TODO what here?
				}
	}
	return 0;
}
int optAlgThreadRunner(optAlgThreadParam &options, size_t threads){

	pthread_t *thread_id;
	//TODO change it!!
	try{
		smart2DArray<pthread_t> smartThreadID(1,threads);
		thread_id = (smartThreadID.getPtr())[0];


		for(size_t i=0;i<threads;i++){

			pthread_create( &thread_id[i], NULL, optAlgThread, (void*)(&options));
		}

		for(size_t i=0;i<threads;i++){

			pthread_join( thread_id[i], NULL);

		}
	}catch(std::bad_alloc &e){
		throw;//TODO what here?
	}
	return 0;
}
